package com.perfma.xtest.cmd;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.*;

public class OSExecutor {

    private static final Logger logger = LoggerFactory.getLogger(OSExecutor.class);

    private ExecutorService pool = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 10, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>());

    public ExecuteResult executeCommand(String command) {
        return executeCommand(command, null);
    }

    public ExecuteResult executeCommand(String command, Object timeout) {
        Process process = null;
        InputStream pIn = null;
        InputStream pErr = null;
        StreamInfo outputInfo = null;
        StreamInfo errorInfo = null;
        Future<Integer> executeFuture = null;
        try {
            String[] env = new String[1];
            env[0] = "tagName=recorder.log";
            process = Runtime.getRuntime().exec(command, env, new File(JobFileAppender.logPath));
            final Process p = process;
            pIn = process.getInputStream();
            outputInfo = new StreamInfo(pIn, "OUTPUT");
            outputInfo.start();

            pErr = process.getErrorStream();
            errorInfo = new StreamInfo(pErr, "ERROR");
            errorInfo.start();

            Callable<Integer> call = new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    p.waitFor();
                    return p.exitValue();
                }
            };

            executeFuture = pool.submit(call);
            int exitCode = 0;
            if (timeout instanceof Integer) {
                logger.debug("任务执行设定超时时间为:{}", timeout);
                exitCode = executeFuture.get(Long.parseLong(timeout + ""), TimeUnit.SECONDS);
            }
            return new ExecuteResult(true, outputInfo.getContent());

        } catch (IOException ex) {
            String errorMessage = "The command [" + command + "] execute failed.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(false, "脚本执行失败");
        } catch (TimeoutException ex) {
            String errorMessage = "The command [" + command + "] timed out.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(false, "脚本运行超时");
        } catch (ExecutionException ex) {
            String errorMessage = "The command [" + command + "] did not complete due to an execution error.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(false, "脚本执行失败");
        } catch (InterruptedException ex) {
            String errorMessage = "The command [" + command + "] did not complete due to an interrupted error.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(false, "脚本执行被中断");
        } finally {
            if (executeFuture != null) {
                try {
                    executeFuture.cancel(true);
                } catch (Exception ignore) {
                    ignore.printStackTrace();
                }
            }
            if (pIn != null) {
                this.closeQuietly(pIn);
                if (outputInfo != null && !outputInfo.isInterrupted()) {
                    outputInfo.interrupt();
                }
            }
            if (pErr != null) {
                this.closeQuietly(pErr);
                if (errorInfo != null && !errorInfo.isInterrupted()) {
                    errorInfo.interrupt();
                }
            }
            if (process != null) {
                process.destroy();
            }
        }
    }

    public ExecuteResult executeCommand(String[] commands, Object timeout) {
        Process process = null;
        InputStream pIn = null;
        InputStream pErr = null;
        StreamInfo outputInfo = null;
        StreamInfo errorInfo = null;
        Future<Integer> executeFuture = null;
        try {
            String[] env = new String[1];
            env[0] = "tagName=recorder.log";
            process = Runtime.getRuntime().exec(commands, env, new File(JobFileAppender.logPath));
            final Process p = process;
            pIn = process.getInputStream();
            outputInfo = new StreamInfo(pIn, "OUTPUT");
            outputInfo.start();

            pErr = process.getErrorStream();
            errorInfo = new StreamInfo(pErr, "ERROR");
            errorInfo.start();

            Callable<Integer> call = new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    p.waitFor();
                    return p.exitValue();
                }
            };

            executeFuture = pool.submit(call);
            int exitCode = 0;
            if (timeout instanceof Integer) {
                logger.debug("任务执行设定超时时间为:{}", timeout);
                exitCode = executeFuture.get(Long.parseLong(timeout + ""), TimeUnit.SECONDS);
            }
            return new ExecuteResult(true, outputInfo.getContent());

        } catch (IOException ex) {
            String errorMessage = "The command [" + format(commands) + "] execute failed.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(false, "脚本执行失败");
        } catch (TimeoutException ex) {
            String errorMessage = "The command [" + format(commands) + "] timed out.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(false, "脚本运行超时");
        } catch (ExecutionException ex) {
            String errorMessage = "The command [" + format(commands) + "] did not complete due to an execution error.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(false, "脚本执行失败");
        } catch (InterruptedException ex) {
            String errorMessage = "The command [" + format(commands) + "] did not complete due to an interrupted error.";
            logger.error(errorMessage, ex);
            return new ExecuteResult(false, "脚本执行被中断");
        } finally {
            if (executeFuture != null) {
                try {
                    executeFuture.cancel(true);
                } catch (Exception ignore) {
                    ignore.printStackTrace();
                }
            }
            if (pIn != null) {
                this.closeQuietly(pIn);
                if (outputInfo != null && !outputInfo.isInterrupted()) {
                    outputInfo.interrupt();
                }
            }
            if (pErr != null) {
                this.closeQuietly(pErr);
                if (errorInfo != null && !errorInfo.isInterrupted()) {
                    errorInfo.interrupt();
                }
            }
            if (process != null) {
                process.destroy();
            }
        }
    }

    private String format(String[] commands){
        StringBuilder sb = new StringBuilder();
        for(String st : commands){
            sb.append(st).append('\n');
        }

        return sb.toString();
    }

    private void closeQuietly(Closeable c) {
        try {
            if (c != null) {
                c.close();
            }
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
}

